<?php
/**
 * Copyright 2011  SURFfoundation
 * 
 * This file is part of ESCAPE.
 * 
 * ESCAPE is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 * 
 * ESCAPE is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 * 
 * You should have received a copy of the GNU General Public License
 * along with ESCAPE.  If not, see <http://www.gnu.org/licenses/>.
 * 
 * For more information:
 * http://escapesurf.wordpress.com/
 * http://www.surffoundation.nl/
 * 
 * Contact: d.vierkant@utwente.nl
 * 
 * @license http://www.gnu.org/licenses/gpl.html GNU GPLv3
 */
?>
<?php
/**
 * Change type
 * 
 * Change the type of an object.
 */
class escape_repositoryui_action_ChangeType extends escape_repositoryui_Action
{
	public function handleRequest()
	{
		/* @var $repository escape_repository_Repository */
		$repository =& $this->requestAttributes['repository'];
		/* @var $repositoryUi escape_repositoryui_RepositoryUi */
		$repositoryUi =& $this->requestAttributes['repositoryUi'];
		
		// fetch the ID from the request
		$objectId = $_REQUEST['actionValue'];
		$objectUri = $repository->convertDataObjectIdToObjectUri($objectId);
		
		$repositorySchema =& $repositoryUi->repository->repositorySchema;
		
		// fetch the object and the aggregation it belongs to
		$aggregation = null;
		try {
			$oreObject = $repository->getOreObjectByUri($objectUri);
			$this->requestAttributes['oreObject'] =& $oreObject;
		}
		catch(Exception $exception)
		{
			$repositoryUi->displayErrorPage($exception->getMessage());
			return null;
		}
		$aggregation = $oreObject->getAggregation();
		$this->requestAttributes['aggregation'] =& $aggregation;
		
		// check if the user has the right permissions
		$user = $repositoryUi->getUser();
		$userIsAuthorized = false;
		if($user)
		{
			$userUri = $user->getUri();
			$userIsAdministrator = $user->hasSystemRole(escape_repository_User::$role_administrator);
			if($userIsAdministrator || $aggregation->hasEditor($userUri) || $aggregation->hasOwner($userUri))
			{
				$userIsAuthorized = true;
			}
		}
	
		// redirect the user to the login page if he/she doesn't have the right permission
		if(!$userIsAuthorized)
		{
			// user is not authorized to edit this resource, redirect to login page
			$repositoryUi->redirectToLoginForAction($this->requestAttributes['actionName'], $objectId);
			return null;
		}

		// it's not possible to change the type of an aggregation or relation annotation
		if($oreObject instanceof escape_repository_OreAggregation)
		{
			$repositoryUi->displayErrorPage('Aggregation type changes are not allowed.');
			return null;
		}
		else if($oreObject->hasRdfType('http://purl.utwente.nl/ns/escape-annotations.owl#RelationAnnotation'))
		{
			$repositoryUi->displayErrorPage('Relation annotation type changes are not allowed.');
			return null;
		}
		
		// check if a new type is selected
		$newRdfType = $_REQUEST['classUri'];
		
		if($newRdfType != '')
		{
			// new type selected
			$this->requestAttributes['newRdfType'] = $newRdfType;

			// check for properties which are incompatible with the new type

			// first check the outgoing relations
			$properties = $oreObject->getProperties();
						
			$subject = $oreObject->getUri();
			
			// exclude the title predicate, the title will be converted later on
			$titleTriple = $oreObject->getTitle(true);
			$titlePredicate = $titleTriple['P'];
			
			// get a list of allowed properties for the new type
			$newAllowedClassPropertyList = $repositorySchema->getAllowedClassPropertyListWithDetails($newRdfType, true);
			$newAllowedProperties = array();
			foreach($newAllowedClassPropertyList as $propertyUri => $value)
			{
				$newAllowedProperties[] = $propertyUri;
				// include all sub properties
				$newAllowedProperties = array_merge($newAllowedProperties, $repositorySchema->getAssignableSubPropertyUriList($propertyUri));
			}
			$newAllowedProperties = array_unique($newAllowedProperties);

			// check if the existing properties are allowed for the new type
			$incompatibleStatements = simplerdf_RdfDocument::createRdfDocument();
			$predicates = $properties->getPredicates();
			foreach($predicates as $predicate)
			{
				// exclude title predicates and non-assignable properties
				if($predicate != $titlePredicate && !$repositorySchema->isNonAssignableProperty($predicate) && !in_array($predicate, $newAllowedProperties))
				{
					// add incompatible properties to an RDF document
					$incompatibleStatementsRdfResource =& $incompatibleStatements->getResourceBySubject($subject);
					if($incompatibleStatementsRdfResource === null)
					{
						$incompatibleStatementsRdfResource =& $incompatibleStatements->createResource($subject);
					}
					$incompatibleStatementsRdfResource->addStatement($predicate, $properties->getObjects($predicate));
				}
			}
			
			// fetch incoming statements
			$incomingStatements = $oreObject->getIncomingStatements();
			
			$rdfResources = $incomingStatements->getResources();
			foreach($rdfResources as $rdfResource)
			{
				/* @var $rdfResource simplerdf_RdfResource */
				$predicates = $rdfResource->getPredicates();
				foreach($predicates as $predicate)
				{
					// exclude non-assignable properties
					if(!$repositorySchema->isNonAssignableProperty($predicate))
					{
						// check the allowed range URIs
						$rangeUris = $repositorySchema->getRangeUris($predicate, true);
						
						$allowedRangeUris = array();
						foreach($rangeUris as $rangeUri)
						{
							$allowedRangeUris[] = $rangeUri;
							// add sub classes
							$allowedRangeUris = array_merge($allowedRangeUris, $repositorySchema->getSubClassUriList($rangeUri));
						}
						$allowedRangeUris = array_unique($allowedRangeUris);
						
						if(!in_array($newRdfType, $allowedRangeUris))
						{
							// add incompatible properties to an RDF document
							$incompatibleStatementsRdfResource =& $incompatibleStatements->getResourceBySubject($rdfResource->getSubject());
							if($incompatibleStatementsRdfResource === null)
							{
								$incompatibleStatementsRdfResource =& $incompatibleStatements->createResource($rdfResource->getSubject());
							}
							$incompatibleStatementsRdfResource->addStatement($predicate, $subject);
						}
					}
				}
			}

			$this->requestAttributes['incompatibleStatements'] =& $incompatibleStatements;
			
			if($incompatibleStatements->getResourceCount() > 0)
			{
				// incompatible statements found, fetch the titles of the objects with incompatible statements
				$objectTitles = array();
				$rdfResources = $incompatibleStatements->getResources();
				foreach($rdfResources as $rdfResource)
				{
					try
					{
						$object = $repository->getOreObjectByUri($rdfResource->getSubject());
						$objectTitles[$rdfResource->getSubject()] = $object->getTitle();
					}
					catch (Exception $ex)
					{
						$objectTitles[$rdfResource->getSubject()] = 'Invalid object ' . $repository->convertObjectUriToDataObjectId($rdfResource->getSubject());
					}
				}
	
				$this->requestAttributes['objectTitles'] =& $objectTitles;
				
				// show the incompatible statements
				return 'TypeChangeIncompatible';
			}
			else
			{
				// no incompatible statements found
				if($_POST['confirm'] == '1')
				{
					// user confirmed the change, perform the change

					// convert the title
					$newTitlePredicates = $repositorySchema->getClassTitlePropertyUriList($newRdfType);
					$newTitlePredicate = $newTitlePredicates[0];
					
					$titles = $properties->getObjects($titlePredicate);
					$properties->removeStatement($titlePredicate);
					$properties->addStatement($newTitlePredicate, $titles);
					
					// change the RDF type
					$properties->setRdfType($newRdfType);
					
					// save the changes
					$oreObject->setProperties($properties);

					// redirect the user to the edit screen
					$returnUrl = $repository->config['server_url'] . $repositoryUi->getActionLinkById('edit', $oreObject->getId());
					if($repositoryUi->isGadgetMode())
					{
						$returnUrl .= '?gadget=1';
					}
					header('Location: ' . $returnUrl);
					
					return null;
				}
				else
				{
					// no confirmation given, ask the user for a confirmation
					return 'TypeChangeConfirm';
				}
			}
		}
		else
		{
			// no type selected, show the type selection screen
			$rangeUris = array('http://purl.utwente.nl/ns/escape-system.owl#Thing');
			$classSelectorTrees = $repositoryUi->getClassSelectorTrees($rangeUris);
			$this->requestAttributes['classSelectorTrees'] =& $classSelectorTrees;
			
			return 'TypeChangeSelect';
		}
	}
}